package tpanalisis.main.java;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import com.sun.org.apache.xerces.internal.impl.xs.identity.Selector.Matcher;

import sun.misc.Regexp;

/**
 * Esta clase va a tener el codigo fuente de lo que llamemos funcion
 * (contructores, metodos, etc) y los valores de los diferentes analisis
 * realizados a la funcion
 * 
 * @author YoSeMiThE
 * 
 */
public class Funcion {

	private String nombre;
	private ArrayList<String> codigo;
	private Integer cantidadLineasDeCodigo;
	private Integer porcentajeComentarios;
	private Integer fanIn;
	private Integer fanOut;
	private Integer complejidadCiclomatica;

	public Funcion() {
		this.codigo = new ArrayList<String>();
	}

	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public ArrayList<String> getCodigo() {
		return codigo;
	}

	public void setCodigo(ArrayList<String> codigo) {
		this.codigo = codigo;
	}

	public void addCodigo(String linea) {
		this.getCodigo().add(linea);
	}

	public Integer getCantidadLineasDeCodigo() {
		return cantidadLineasDeCodigo;
	}

	public void setCantidadLineasDeCodigo() {
		String buffer;
		int contador = 0;
		for (int i = 0; i < this.codigo.size(); i++) {
			buffer = this.codigo.get(i).toString();
			if (!buffer.isEmpty())
				contador++;
		}
		this.cantidadLineasDeCodigo = contador;
	}

	public Integer getPorcentajeComentarios() {
		return porcentajeComentarios;
	}

	public void setPorcentajeComentarios() {
		Integer lineasComentadas = 0, buscarFinMultiLinea = 0;
		Integer totalLineas = this.getCantidadLineasDeCodigo();
		CharSequence comentarioSimple = "//";
		CharSequence inicioMultiLinea = "/*";
		CharSequence finMultiLinea = "*/";
		String buffer;
		Iterator<String> it = codigo.iterator();

		while (it.hasNext()) {
			buffer = it.next().toString();
			if (buscarFinMultiLinea == 0 && buffer.contains(inicioMultiLinea))
				buscarFinMultiLinea++;

			if (buscarFinMultiLinea == 0 && buffer.contains(comentarioSimple))
				lineasComentadas++;

			if (buscarFinMultiLinea > 0 && !buffer.isEmpty())
				lineasComentadas++;

			if (buffer.contains(finMultiLinea))
				buscarFinMultiLinea--;
		}
		this.porcentajeComentarios = 100 * lineasComentadas / totalLineas;
	}

	public Integer getFanIn() {
		return fanIn;
	}

	public void setFanIn(List<Funcion> funciones) {
		Set<Funcion> funcionesNoRepetidas = new HashSet<Funcion>(funciones);
		// Fan in: cantidad de veces que una funcion llama a otras funciones
		int contador = 0;
		// Set<Funcion> setFunciones = new HashSet<Funcion>(funciones);
		for (String linea : this.getCodigo()) {
			for (Funcion funcion : funcionesNoRepetidas) {
				System.out.println(funcion.getNombre());
				if (linea.contains(funcion.getNombre() + "(")) {
					contador++;
					System.out.println("\t\tEXITO");
				}
			}
		}
		this.fanIn = contador - 1;
	}

	public Integer getFanOut() {
		return fanOut;
	}

	public void setFanOut(List<Funcion> funciones) {
		Set<Funcion> funcionesNoRepetidas = new HashSet<Funcion>(funciones);
		// Fan out: cantidad de veces que una funcion es llamada, en todo el
		// grupo de funciones
		String nombre = this.getNombre();
		int contador = 0;

		for (Funcion funcion : funcionesNoRepetidas) {
			for (String linea : funcion.getCodigo()) {
				if (linea.contains(nombre))
					contador++;
			}
		}

		this.fanOut = contador - 1;
	}

	public Integer getComplejidadCiclomatica() {
		return complejidadCiclomatica;
	}

	public void setComplejidadCiclomatica() {
		int contador = 0, comentarioMultiLinea = 0, indice = 0;

		for (String linea : this.getCodigo()) {
			if (linea.contains("/*")) {
				comentarioMultiLinea = 1;
				if (Pattern.matches(".*(if|while|switch).*(\\/\\*)", linea))
					contador++;
			}
			
			indice = 0;

			if (comentarioMultiLinea == 0) {
				if (Pattern.matches(".*(\\s*)if(\\s*)(\\(.*\\)).*", linea) && !Pattern.matches(".*(\\/\\/).*if.*", linea))
					contador++;

				if (Pattern.matches(".*(\\s*)while(\\s*)(\\(.*\\)).*", linea) && !Pattern.matches(".*(\\/\\/).*while.*", linea))
					contador++;
				
				if (Pattern.matches(".*(\\s*)switch(\\s*)(\\(.*\\)).*", linea) && !Pattern.matches(".*(\\/\\/).*switch.*", linea))
					contador++;
				
				if (Pattern.matches(".*(\\s*)for(\\s*)(\\(.*\\)).*", linea) && !Pattern.matches(".*(\\/\\/).*for.*", linea))
					contador++;
				
				indice = linea.indexOf("&&");
				while(indice != -1) {
					contador++;
					if(indice + 2 <= linea.length())
						indice += 2;
					if(linea.substring(indice).contains("&&")) {
						indice += linea.substring(indice).indexOf("&&");
					}
					else
						indice = -1;
				}
				
				indice = linea.indexOf("||");
				while(indice != -1) {
					contador++;
					if(indice + 2 <= linea.length())
						indice += 2;
					if(linea.substring(indice).contains("||")) {
						indice += linea.substring(indice).indexOf("||");
					}
					else
						indice = -1;
				}
				
			} else if (linea.contains("*/")) {
				comentarioMultiLinea = 0;
				if (Pattern.matches(".*(\\*\\/).*(if|while|switch).*", linea))
					contador++;
			}
		}
		this.complejidadCiclomatica = contador + 1;

	}

	public void procesarFuncion(List<Funcion> funciones) {
		this.setCantidadLineasDeCodigo();
		this.setPorcentajeComentarios();
		this.setFanIn(funciones);
		this.setFanOut(funciones);
		this.setComplejidadCiclomatica();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Funcion other = (Funcion) obj;
		if (nombre == null) {
			if (other.nombre != null)
				return false;
		} else if (!nombre.equals(other.nombre))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return this.getNombre();
	}

}
